/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package phd.dupenois.changeidentifiers.application.graphical;


import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import phd.dupenois.changeidentifiers.ChangeIdentifier;
import phd.dupenois.changeidentifiers.ChangeIdentifierListener;
import phd.dupenois.changeidentifiers.ChangeIdentifierSet;
import phd.dupenois.changeidentifiers.application.graphical.frames.MainFrame;
import phd.dupenois.changeidentifiers.application.graphical.frames.SetupDialog;
import phd.dupenois.changeidentifiers.application.graphical.testing.DatabaseTestManager;
import phd.dupenois.changeidentifiers.dotpatterns.DotPattern;
import phd.dupenois.changeidentifiers.engine.ChangeIdentifierApplication;
import phd.dupenois.changeidentifiers.engine.testing.TestingInitialisationException;
import phd.dupenois.changeidentifiers.exceptions.ChangeIdentifierSetNotValidException;
import phd.dupenois.changeidentifiers.footprints.Footprint;
import phd.dupenois.changeidentifiers.footprints.algorithms.FootprintAlgorithm;

/**
 *
 * @author mpd209
 */
public class GraphicalApplication extends ChangeIdentifierApplication implements Runnable, ChangeIdentifierListener{

    private boolean loadedAllDotPatterns;
    private MainFrame frame;
    private int dotPatternsTotal;
    private SetupDialog setupDialog;

    private Class<? extends FootprintAlgorithm> algorithmClass; 
    private ChangeIdentifierSet changeIdentifierSet;
    private String[] dotPatternStream;

    private String testTitle;
    private String testDescription;

    private DatabaseTestManager testManager;

    private int pause = 0;

    private int runCount;

    private boolean running;

    private boolean rerunSetup;

    public GraphicalApplication(){
        super();
        try {
            UIManager.setLookAndFeel(
                UIManager.getSystemLookAndFeelClassName());
        }
        catch (UnsupportedLookAndFeelException e) {
           // handle exception
        }
        catch (ClassNotFoundException e) {
           // handle exception
        }
        catch (InstantiationException e) {
           // handle exception
        }
        catch (IllegalAccessException e) {
           // handle exception
        }
        this.rerunSetup = true;
        this.running = false;
        this.runCount = 0;

    }

    public void runEngine(){
        this.frame.disableReruns();
        try {
            String title = testTitle;
            if(runCount>0) title += "["+runCount+"]";
            //System.out.println("Engine not null: "+(this.engine!=null)+" Class: "+(this.engine!=null?this.engine.getClass().getSimpleName():""));
            this.engine.setupTestManager(testManager, title, testDescription);
        } catch (TestingInitialisationException ex) {
            ex.printStackTrace();
            System.exit(1);
        }

        Vector<DotPattern> patterns = new Vector<DotPattern>();
        for(String dotPatternFilename: dotPatternStream){
            DotPattern dp = new DotPattern();
            try {
                dp.load(dotPatternFilename);
                patterns.add(dp);
            } catch (FileNotFoundException ex) {
                exceptionThrown(ex);
            } catch (IOException ex) {
                exceptionThrown(ex);
            }
        }


        try {
            FootprintAlgorithm algorithm = algorithmClass.newInstance();
            this.engine.initialise(algorithm, changeIdentifierSet, pause);
            new Thread(this.engine).start();
        } catch (InstantiationException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        }
        //Add Dot Patterns
        for(DotPattern dp: patterns){
            this.engine.loadDotPattern(dp);
        }
        this.loadedAllDotPatterns = true;
    }

    public void setup(){
        if(frame==null){
            frame = new MainFrame(this);
            if(pause>=100)frame.haltScrollOnManualUse();
            frame.setVisible(true);
        }

        if(setupDialog==null)setupDialog = new SetupDialog(frame, true);
        setupDialog.setVisible(true);


//        DatabaseTestManager testManager = new DatabaseTestManager("secamsql.ex.ac.uk", "pccat", "pccat", "pccat");
        this.testManager = new DatabaseTestManager(
                setupDialog.getHost(), setupDialog.getDatabase(),
                setupDialog.getUsername(), new String(setupDialog.getPassword()));
        testTitle = setupDialog.getTestTitle();
        testDescription = setupDialog.getTestDescription();
        setupDialog.clearPassword();
       

        algorithmClass = setupDialog.getAlgorithmClass();
        changeIdentifierSet = setupDialog.getChangeIdentifierSet();
        dotPatternStream = setupDialog.getDotPatternStream();
        dotPatternsTotal = dotPatternStream.length;
        loadedAllDotPatterns = false;
        try {
            changeIdentifierSet.initialise(this);
        } catch (ChangeIdentifierSetNotValidException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    public void run() {
        this.running = true;
        if(rerunSetup) setup();
        runEngine();
        
    }

    public void rerunWithSetup(){
        if(isRunning()) return;
        this.runCount = 0;
        rerunSetup = true;
        new Thread(this).start();
    }

    public void rerun(){
        if(isRunning()) return;
        this.runCount++;
        rerunSetup = false;
        new Thread(this).start();
    }

    private StackTraceElement getFirstMyElement(StackTraceElement[] stackTrace){
        StackTraceElement found = null;
        for(int i=0, max = stackTrace.length; i <max && (found==null);i++){
           if(stackTrace[i].getClassName().contains("phd.dupenois")){
               found = stackTrace[i];
           }
        }
        return found;
    }
    public void exceptionThrown(Exception ex) {
        StackTraceElement element = getFirstMyElement(ex.getStackTrace());
        String msg = ex.getMessage();
        if(element != null){
            msg += " -- "+element.getClassName()+"::"+element.getMethodName()+"["+element.getLineNumber()+"]";
        }
        frame.error("Exception Thrown: "+msg);
//        System.out.println(+ex.getMessage());
    }

    public void dotPatternBufferIsEmpty() {
        frame.warning("Dot Pattern Buffer is Empty");
        //System.out.println("Dot Pattern Buffer Is Empty");
        if(this.loadedAllDotPatterns) this.engine.stop();
    }
    public void drawDotPattern(DotPattern dotPattern) {
        frame.drawDotPattern(dotPattern);
        frame.incrementProgessBar();
    }
    public void drawFootprint(Footprint footprint) {
        frame.drawFootprint(footprint);
        frame.success("Rebuild Footprint");
    }

    public void startedEngine(long time) {
        frame.setupProgressBar(dotPatternsTotal);
        frame.message("Started Engine");
    }

    public void finishedWithChangeIdentifiers(long timeTaken, int redrawCount){
        frame.message("Finished With Change Identifiers "+(timeTaken/1000)+"s. ["+timeTaken+"ms]");
        frame.message("Redrew "+redrawCount+" times");
    }
    public void finishedEngine() {
        frame.message("Engine Finished");
        rerunSetup = false;
        frame.enableReruns();
        this.running = false;
    }

    public void changeIdentifierBrokenMaxFail(ChangeIdentifier identifier, int maxFails) {
    }

    public void changeIdentifierBrokenOwnThreshold(ChangeIdentifier identifier, double threshold, double value, boolean redraw) {
    }

    public void startedChangeIdentifierSet(long time) {
    }

    public void finishedChangeIdentifierSet(long timeTaken) {
    }

    /**
     * @return the running
     */
    public boolean isRunning() {
        return running;
    }

    public void startedRecordingTests() {
        frame.startProgressBarIndeterminate();
        frame.message("Started Recording Test");
    }

    public void finishedRecordingTests() {
        frame.stopProgressBarIndeterminate();
        frame.message("Finished Recording Test");
    }

    public void startedRunningTestsWithoutChangeIdentifiers(long time) {
        frame.setupProgressBar(dotPatternsTotal);
        frame.message("Started running tests without change identifiers.");
    }
    public void finishedRunningTestsWithoutChangeIdentifiers(long timeTaken, int redrawCount, long difference, int redrawCountDifference) {
        frame.message("Finished running tests without change identifiers "+(timeTaken/1000)+"s. ["+timeTaken+"ms]");
        frame.message("Redrew "+redrawCount+" times");
        if(difference >= 0){
            frame.success("Without change identifiers took "+difference+"ms longer than with.");
        }else{
            frame.error("With change identifiers took "+(-difference)+"ms longer than without.");
        }
        if(redrawCountDifference > 0){
            frame.success("Without change identifiers redrew "+redrawCountDifference+" more times than with.");
        }else{
            frame.error("With change identifiers redrew "+(-redrawCountDifference)+" more times than without.");
        }
    }






}
